/**
 * @file       godemool/codec.h
 * @brief      coder/decoder objects class.
 * @author     ben "boin" bonoeil
 * @date       2015-02
 */

/* Copyright (c) 2007-2015, ben "boin" bonoeil */

#ifndef GODEMOOL_CODEC_H
#define GODEMOOL_CODEC_H

#include "godemool/csgo.h"
#include "godemool/types.h"

#include <cstdint>
#include <string>
#include <memory>
#include <vector>

namespace boin {

  struct PlayerInfo
  {
    enum {
      CUSTOM_FILES = 4,
      NAME_LEN     = 128,
      GUID_LEN     = 32+1,
      INVALID_UID  = -1,
      INVALID_FID  = -1,
    };

    PlayerInfo();
    void Clear();

    bool operator == ( uint64_t p_xid ) const { return xid == p_xid; }
    bool operator == ( uint32_t p_uid ) const { return uid == p_uid; }

    uint64_t version; // version for future compatibility
    uint64_t xid;     // network xuid
    uint32_t uid;     // local server user ID, unique while server is running
    uint32_t fid;     // friends identification number
    std::string guid; // global unique player identifer
    std::string name; // scoreboard information
    std::string friendsName; // friends name
    bool isFake; // true, if player is a bot controlled by game.dll
    bool isHltv; // true if player is the HLTV proxy
    std::vector<uint32_t> customFilesCrc; // custom files CRC for this player
    uint16_t dlFilesCount; // Incremented on server download
  };

  struct PlayerList : public std::vector<PlayerInfo>
  {
    template <typename T> PlayerInfo * Find(T something) {
      for (auto it = begin(); it != end(); ++it)
        if ((*it) == something)
          return &(*it);
      return nullptr;
    }
    PlayerInfo * Find(uint32_t uid);
    PlayerInfo * Find(uint64_t xid);
    PlayerInfo & Update(const PlayerInfo & pi);
  };

  class WalkData {
  public:
    virtual ~WalkData() {}
    virtual int Size() const = 0;
    virtual uint8_t At(const int i) const = 0;
  };

  class WalkStringRef : public WalkData {
  protected:
    const std::string & ref;
  public:
    WalkStringRef(const std::string & str) : ref(str) {}
    int Size() const { return ref.size(); }
    uint8_t At(const int i) const { return ref.at(i); }
  };

  class ByteArray: public WalkData, public std::vector<uint8_t> {
  public:
    virtual int Size() const { return size(); }
    virtual uint8_t At(const int i) const { return at(i); }
  };

  struct StringTable {
    struct String {
      std::string name;
      ByteArray data;
    };
    std::string name;
    std::vector<String> strdata;
    std::vector<String> usrdata;
  };

  struct SendTable {
    uint32_t type;
    uint32_t size;
    uint8_t *data;
    CSVCMsg_SendTable msg;
    void Parse() { msg.ParseFromArray(data,size); }
  };

  struct ServerClass {
    uint16_t idx;
    uint16_t dtidx;
    std::string name;
    std::string dtname;

    // struct FlattenedPropEntry
    //   {
    //     FlattenedPropEntry( const CSVCMsg_SendTable::sendprop_t *prop, const CSVCMsg_SendTable::sendprop_t *arrayElementProp )
    //       : m_prop( prop )
    //       , m_arrayElementProp( arrayElementProp )
    //{
    //}
    //     const CSVCMsg_SendTable::sendprop_t *m_prop;
    //     const CSVCMsg_SendTable::sendprop_t *m_arrayElementProp;
    // };
    // std::vector< FlattenedPropEntry > flatprops;
  };

  class MsgData : public WalkData {
  public:
    MsgData(): msg(nullptr) {};
    ~MsgData() {
      if (msg)
        delete msg;
    }
    MsgData(const MsgData & from)
      : type(from.type), size(from.size), data(from.data) {
      if (!from.msg)
	msg = nullptr;
      else {
	msg = from.msg;
	from.msg = nullptr; // Dirty: loose pointer ownership
	//const_cast<MsgData *>(&from)->msg = nullptr;
      }
    }

    virtual int Size() const { return size; }
    virtual uint8_t At(const int i) const {
      assert(uint32_t(i)<size); return data[i];
    }

    uint32_t type;
    uint32_t size;
    uint8_t *data;
    mutable google::protobuf::Message * msg;
  };

  /** Base type. */
  struct CodecType { virtual ~CodecType() {} /*Needs to be polymorphic*/ };

  /** Raw data. */
  class RawData : public CodecType, public WalkData {
  public:
    uint32_t size;
    uint8_t *data;
    virtual int Size() const { return size; }
    virtual uint8_t At(const int i) const {
      assert(uint32_t(i)<size); return data[i];
    }
  };

  /** Custom data. */
  struct CustomData : public RawData {};

  /** User Command. */
  struct UserCommand : public RawData {
    void DeltaRead();
    union {
      unsigned int any;
      struct {
        unsigned int id      : 1;
        unsigned int tick    : 1;
        unsigned int vect    : 9; // {view/aim/move}.{x/y/z}
        unsigned int buttons : 1;
        unsigned int impulse : 1;
        unsigned int weapon  : 1;
        unsigned int subwpn  : 1;
        unsigned int mousedx : 1;
        unsigned int mousedy : 1;
      };
    } has;
    uint32_t seqOut, id, tick;
    boin::Vector vec[3];
    uint32_t buttons;
    uint8_t impulse;
    uint16_t weapon, subwpn;
    int16_t mousedx, mousedy;
  };

  /** Console Command. */
  struct ConCommand: public CodecType, public std::string {};

  /** String tables. */
  struct StringTables : public CodecType, std::vector<StringTable> {};

  /** Data tables. */
  struct DataTables : public CodecType {
    std::vector<SendTable> sendtables;
    std::vector<ServerClass> srvclasses;
    void clear() { sendtables.clear(); srvclasses.clear(); }
  };

  /** Network/demo messages. */
  struct Messages: public CodecType, std::vector<MsgData> {
    uint32_t seqInp, seqOut;
  };

}

#endif

/* Local Variables:  */
/* mode: c++         */
/* End:              */
